/*
 * Copyright (C) 2000-2008 Jens Nixdorf and various contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies of the Software, its documentation and marketing & publicity
 * materials, and acknowledgment shall be given in the documentation, materials
 * and software packages that this Software was used.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
package org.mopore.jat.gui;

import java.awt.CheckboxMenuItem;
import java.awt.Menu;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mopore.jat.core.CorePlatformPlugin;
import org.mopore.jat.plugin.IJatPlugin;
import org.mopore.jat.plugin.IJatPluginInternal;
import org.mopore.jat.plugin.IPluginContextInternal;
import org.mopore.jat.plugin.IPluginFocusListener;
import org.mopore.jat.plugin.IPluginGui;
import org.mopore.jat.plugin.ITerminatedListener;
import org.mopore.jat.plugin.PluginException;

public class PopUpHandler implements ITerminatedListener, 
	IPluginFocusListener {

	private final PopupMenu popupMenu;

	private final Log log = LogFactory.getLog(getClass().getName());

	private final IPluginContextInternal pluginContext;

	private boolean pluginsDirty = false;

	private boolean focusDirty = false;

	private List<CheckboxMenuItem> focusCheckBoxList;


	public PopUpHandler(IPluginContextInternal pluginContext, PopupMenu popupMenu ) {

		this.pluginContext = pluginContext;
		pluginContext.addPluginFocusListener( this );
		pluginsDirty = true;
		focusDirty = true;
		this.popupMenu = popupMenu;
		focusCheckBoxList = new LinkedList<CheckboxMenuItem>();
	}
	


	private MenuItem[] getMenuFocusActions() {

		String pluginName = pluginContext.getPluginNameWithFocus();
		MenuItem[] elements = null; 

		if ( pluginName != null ) {
			Map<String, IJatPluginInternal> pluginMap = pluginContext
					.getPlugInMap();
	
			IPluginGui plugin = pluginMap.get(pluginName);
				elements = plugin.getMenuFocusActions();
		}
		
		if (elements == null) {
			elements = new MenuItem[0];
		}
		return elements;
	}




	public void buildPopUpMenu() {

		if (pluginsDirty || focusDirty) {
			log.debug("build up menu");

			try {
				popupMenu.removeAll();

				boolean focuasActionsAdded = addMenuFocusActions(popupMenu);
				if ( focuasActionsAdded ) {
					popupMenu.addSeparator();
				}

				
				addDashboard( popupMenu );
				addPluginFocus( popupMenu );
				addMenuActions(popupMenu);

				popupMenu.addSeparator();
				
				attachCoreSetup();

				MenuItem exitItem = new MenuItem("shut down");
				exitItem.addActionListener(new GlobalShutdownActionListener());
				popupMenu.add(exitItem);

				pluginsDirty = false;
				focusDirty = false;
			} catch (PluginException e) {
				log.error("an error occuered...", e);
				throw new InternalError();
			}
		}
	}


	/**
	 * @param popupMenu
	 */
	private void addDashboard(PopupMenu popupMenu) {
		MenuItem menuItem = new MenuItem( "dashboard" ); 
		
		menuItem.addActionListener( new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				pluginContext.showDashboard();
			}
		});
		popupMenu.add( menuItem );
		
	}



	private void attachCoreSetup() throws PluginException {
		String corePlatformPluginName = CorePlatformPlugin.class.getName();
		IJatPluginInternal corePlatformPlugin = 
			(IJatPluginInternal) pluginContext.getPlugin( corePlatformPluginName );
		
		attachActionsToMenu( corePlatformPlugin, popupMenu );
	}

	private boolean addMenuActions(PopupMenu popup) throws PluginException {

		boolean added = false;
		
		
		Menu actionsMenu = new Menu( "more..." );
		Collection<IJatPluginInternal> plugins = 
			pluginContext.getPlugInMap().values();

		for (IJatPluginInternal plugin : plugins) {
			// skip if core platform plugin
			if ( plugin instanceof CorePlatformPlugin ) {
				continue;
			}
			
			boolean entriesAdded = attachActionsToMenu( plugin, actionsMenu );
			if ( entriesAdded ) {
				added = true;
			}
		}
		
		if ( added ) {
			popup.add( actionsMenu );
		}
		return added;
	}

	
	private boolean attachActionsToMenu(IJatPluginInternal plugin, Menu actionsMenu) throws PluginException {
		
		boolean added = false;
		
		if ( plugin != null ) {
			
			final String displayName = plugin.getDisplayName();
			
			MenuItem pluginBaseAction = new MenuItem( displayName );
			MenuItem[] menuItems = plugin.getMenuActions( pluginBaseAction );
			
			boolean gotSubActions = 
				( menuItems != null ) && ( menuItems.length > 0);
			
			if ( gotSubActions ) {
				Menu subActionMenu = new Menu( displayName );
				 
				for (MenuItem item : menuItems) {
					subActionMenu.add( item );
				}
				actionsMenu.add( subActionMenu );
				added = true;
			}
		}
		
		return added;
		
	}


	private boolean addMenuFocusActions(PopupMenu popup) {
		
		boolean focusActionsAdded = false;

		MenuItem[] items = getMenuFocusActions();

		for (MenuItem element : items) {
			popup.add(element);
			focusActionsAdded = true;
		}
		
		return focusActionsAdded;
	}

	private boolean addPluginFocus(PopupMenu popup) throws PluginException {

		boolean focusSteeringAdded = false;
		
		Menu focusMenu = new Menu("set focus");
		
		List<IJatPluginInternal> plugins = pluginContext.getFocusablePlugins();

		boolean focusSteeringNeeded = plugins.size() > 1;
		if ( focusSteeringNeeded ) {
			
			popup.add( focusMenu );
	
			String focusedName = pluginContext.getPluginNameWithFocus();
			for (IJatPluginInternal plugin : plugins) {
	
				String key = plugin.getPluginName();
	
				String displayName = plugin.getDisplayName();
				boolean checked = key.equals( focusedName );
				CheckboxMenuItem pluginItem = new CheckboxMenuItem(displayName,
						 checked);
				focusCheckBoxList.add( pluginItem );
				pluginItem.addItemListener(new FocusItemListener( key ));
				focusMenu.add(pluginItem);
			}
			focusSteeringAdded = true;
		}

		return focusSteeringAdded;
	}

	
	public void onTerminated(String pluginName) {
		this.pluginsDirty = true;
	}

	public void onFocusGained(IJatPlugin plugin) throws PluginException {
		this.focusDirty = true;

	}

	public void onFocusLost(IJatPlugin plugin) throws PluginException {
		// not needed
	}

	private class FocusItemListener implements ItemListener {

		private final String pluginName;

		public FocusItemListener(String pluginName ) {

			this.pluginName = pluginName;
		}


		@Override
		public void itemStateChanged(ItemEvent e) {
			CheckboxMenuItem source = 
				(CheckboxMenuItem) e.getSource();
			
			for (CheckboxMenuItem current : focusCheckBoxList ) {
				boolean isChecked = ( source == current );
				current.setState( isChecked );
			}
			
			pluginContext.requestFocusChange(pluginName);
			buildPopUpMenu();
		}

	}

	private class GlobalShutdownActionListener implements ActionListener {

		public void actionPerformed(ActionEvent e) {
			pluginContext.requestGlobalShutDown();
		}

	}

	
	public void setPluginsDirty() {
		pluginsDirty = true;
		buildPopUpMenu();
	}
}
